#include <cstdio>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>

//#define WRAP_NAMESPACE
//#define WRAP_GUARD
#define NAMESPACE_NAME "NettleMath"

typedef std::string String;

const String axisNames[] = {
	String("x"), 
	String("y"), 
	String("z"), 
	String("w")
};

const String vecSizeStr[] = {
	String("0"), 
	String("1"), 
	String("2"), 
	String("3"),
	String("4")
};

const String NVectorTypeName("NVector");

bool isNativeOrder(const std::vector<int>& swizzleIds){
	if(swizzleIds.size() < 2)
		return false;

	for(int i = 0; i < (swizzleIds.size() - 1); ++i){
		if(swizzleIds[i + 1] != swizzleIds[i] + 1)
			return false;
	}
	return true;
}

String genSwizzle(int vectorSize, std::vector<int> swizzleIds){
	std::stringstream ss;

	String swName, swParams;
	for(int i = 0; i < swizzleIds.size(); ++i){
		swName += axisNames[swizzleIds[i]];
		swParams += axisNames[swizzleIds[i]] + (i >= (swizzleIds.size() - 1) ? String("") : String(", "));
	}
	ss <<  NVectorTypeName << vecSizeStr[swizzleIds.size()] << "<T>";
	String fullTypeName = ss.str();
	ss.str("");

	std::cout << ss.str();

	if(isNativeOrder(swizzleIds)){
		ss << fullTypeName << "& " << swName << "()";
		ss << "{ ";
		if(swizzleIds.size() != vectorSize)
			ss << "return " <<  "*reinterpret_cast<" << fullTypeName << "*>("  << "reinterpret_cast<T*>(this) + " << swizzleIds[0] << ");";
		else
			ss << "return *this;";
		ss << " }";
	}else{
		//const swizzle
		ss << "const " << fullTypeName << " " << swName << "()" << " const ";
		ss << "{ ";
		ss << "return " << fullTypeName << "(" << swParams << ");";
		ss << " }";
	}
	return ss.str();
}

void genSwizzles(int vectorSize, int swizzleSize, std::vector<String>& out_sw){
	std::vector<String> swizzles;
	std::vector<int> swIds;
	int totalPerm = std::pow(vectorSize, swizzleSize);
	//gen all swizzle permutations
	for(int i = 0; i < totalPerm; ++i){
		swIds.clear();
		for(int j = 0; j < swizzleSize; ++j){
			int val =  (i / static_cast<int>((std::pow(vectorSize,(swizzleSize - j - 1))))) % vectorSize;
			swIds.push_back(val);
		}
		out_sw.push_back(genSwizzle(vectorSize, swIds));
	}
}

void saveVectorSwizzles(int vectorSize, const std::vector<String>& swizzles){
	std::stringstream ss;
	std::stringstream ss_data;
	String indent("");
	ss << NVectorTypeName << vectorSize << "Swizzle.h";
	String fileName = ss.str();
	String filePath = String("./") + fileName;

	FILE* f;
	if(fopen_s(&f, filePath.c_str(), "w")){
		std::cout << "Failed to open file!" << std::endl;
		return;
	}

#ifdef WRAP_GUARD
	std::stringstream ssGuardName;
	ssGuardName << fileName << "__";
	ss_data << "#ifndef " << ssGuardName.str() << std::endl;
	ss_data << "#define " << ssGuardName.str() << std::endl << std::endl;
#endif
#ifdef WRAP_NAMESPACE
	ss_data << "namespace " << NAMESPACE_NAME << " {" << std::endl;
	indent += "    ";
#endif

	for(auto s : swizzles){
		ss_data << indent << s << std::endl;
	}

#ifdef WRAP_NAMESPACE
	ss_data << "}" << std::endl;
#endif
#ifdef WRAP_GUARD
	ss_data << "#endif //" << ssGuardName.str() << std::endl;
#endif

	fwrite(ss_data.str().c_str(), ss_data.str().length(), 1, f);
	fclose(f);
}

int main(){
	int min_vec_dim = 2;
	int max_vec_dim = 4;

	//generate
	for(int i = min_vec_dim; i <= max_vec_dim; ++i){
		std::vector<String> swizzles;
		for(int j = min_vec_dim; j <= i; ++j){
			genSwizzles(i, j, swizzles);
		}
		saveVectorSwizzles(i, swizzles);
	}
	
	std::cout << "Done!" << std::endl;
	return 0;
}

